#ifndef HELPER
#define HELPER
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>
#include <sqlite3.h>
#include <cstring>
#include <cerrno>
#include <sys/stat.h>
#include "Log.hpp"
using namespace std;

class SqliteHelper 
{
public:
    typedef int(*SqliteCallback)(void*,int,char**,char**);
    SqliteHelper(const string &dbfile) : _dbfile(dbfile), _handler(nullptr){}
    
    bool open(int safe_leve = SQLITE_OPEN_FULLMUTEX) 
    {
        //int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
        int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_leve, nullptr);
        if (ret != SQLITE_OK) 
        {
            lg(Fatal, "创建/打开sqlite数据库失败: ", sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }

    bool exec(const string &sql, SqliteCallback cb, void *arg) 
    {
        //int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err)
        int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
        if (ret != SQLITE_OK) 
        {
            lg(Error, "%s 执行语句失败 %s", sql.c_str(), sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }
    void close() 
    {
        //int sqlite3_close_v2(sqlite3*);
        if (_handler) 
            sqlite3_close_v2(_handler);
    }
private:
    string _dbfile;
    sqlite3 *_handler;
};

class StrHelper
{
public:
    static size_t split(const string& str, const string& sep, vector<string>& result)
    {
        size_t index = 0, pos = 0;
        while(index < str.size())
        {
            pos = str.find(sep, index);
            if(pos == string::npos)
            {
                result.push_back(str.substr(index));
                return result.size();
            }
            if(pos == index)
            {
                index = pos + sep.size();
                continue;
            }
            result.push_back(str.substr(index, pos - index));
            index = pos + sep.size();
        }
        return result.size();
    }
};

class UUIDHelper 
{
public:
    static string uuid() 
    {
        random_device rd;
        mt19937_64 gernator(rd());
        uniform_int_distribution<int> distribution(0, 255);
        stringstream ss;
        for (int i = 0; i < 8; i++) 
        {
            ss << setw(2) << setfill('0') << hex << distribution(gernator) ;
            if (i == 3 || i == 5 || i == 7) 
                ss << "-";
        }
        static atomic<size_t> seq(1);
        size_t num = seq.fetch_add(1);
        for (int i = 7; i >= 0; i--) 
        {
            ss << setw(2) << setfill('0') << hex << ((num>>(i*8)) & 0xff);
            if (i == 6) 
                ss << "-";
        }
        return ss.str();
    }
};

class FileHelper 
{
public:
    FileHelper(const string &filename):_filename(filename){}
    bool exists() 
    {
        struct stat st;
        return (stat(_filename.c_str(), &st) == 0);
    }
    size_t size() 
    {
        struct stat st;
        int ret = stat(_filename.c_str(), &st);
        if (ret < 0) 
            return 0;
        return st.st_size;
    }
    bool read(char *body, size_t offset, size_t len) 
    {
        //1. 打开文件
        ifstream ifs(_filename, ios::binary | ios::in); 
        if (ifs.is_open() == false) 
        {
            lg(Error, "%s 文件打开失败！", _filename.c_str());
            return false;
        }
        //2. 跳转文件读写位置
        ifs.seekg(offset, ios::beg);
        //3. 读取文件数据
        ifs.read(body, len);
        if (ifs.good() == false) 
        {
            lg(Error, "%s 文件读取数据失败！！", _filename.c_str());
            ifs.close();
            return false;
        }
        //4. 关闭文件
        ifs.close();
        return true;
    }
    bool read(string &body) 
    {
        //获取文件大小，根据文件大小调整body的空间
        size_t fsize = this->size();
        body.resize(fsize);
        return read(&body[0], 0, fsize);
    }
    bool write(const char *body, size_t offset, size_t len) 
    {
        //1. 打开文件
        fstream fs(_filename, ios::binary | ios::in | ios::out); 
        if (fs.is_open() == false) 
        {
            lg(Error, "%s 文件打开失败！", _filename.c_str());
            return false;
        }
        //2. 跳转到文件指定位置
        fs.seekp(offset, ios::beg);
        //3. 写入数据
        fs.write(body, len);
        if (fs.good() == false) 
        {
            lg(Error, "%s 文件写入数据失败！！", _filename.c_str());
            fs.close();
            return false;
        }
        //4. 关闭文件
        fs.close();
        return true;
    }
    bool write(const string &body) 
    {
        return write(body.c_str(), 0, body.size());
    }
    bool rename(const string &nname) 
    {
        return (::rename(_filename.c_str(), nname.c_str()) == 0);
    }
    static string parentDirectory(const string &filename) 
    {
        size_t pos = filename.find_last_of("/");
        if (pos == string::npos) 
            return "./";  
        string path = filename.substr(0, pos);
        return path;
    }
    static bool createFile(const string &filename) 
    {
        fstream ofs(filename, ios::binary | ios::out); 
        if (ofs.is_open() == false) 
        {
            lg(Error, "%s 文件打开失败！", filename.c_str());
            return false;
        }
        ofs.close();
        return true;
    }
    static bool removeFile(const string &filename) 
    {
        return (::remove(filename.c_str()) == 0);
    }
    static bool createDirectory(const string &path) 
    {
        size_t pos, idx = 0;
        while(idx < path.size()) 
        {
            pos = path.find("/", idx);
            if (pos == string::npos)
                return (mkdir(path.c_str(), 0775) == 0);
            string subpath = path.substr(0, pos);
            int ret = mkdir(subpath.c_str(), 0775);
            if (ret != 0 && errno != EEXIST) 
            {
                lg(Error, "创建目录 %s 失败: %s", subpath.c_str(), strerror(errno));
                return false;
            }
            idx = pos + 1;
        }
        return true;
    }
    static bool removeDirectory(const string &path) 
    {
        string cmd = "rm -rf " + path;
        return (system(cmd.c_str()) != -1);
    }
private:
    string _filename;
};
#endif